{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 理解编程语言"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "简单地说，程序就是人类书写交由机器去执行的一系列指令，而编程语言是书写时遵照的语法规则。\n",
    "\n",
    "编程很像教孩子，计算机就像一个孩子，一开始什么都不懂，也不会自己思考，但非常听话，你教TA怎样就怎样，一旦学会的就不会忘，可以不走样地做很多次不出错。但是如果教的不对，TA也会不折不扣照做。\n",
    "\n",
    "对我们来说，和孩子的教育一样，关键在于理解对方的沟通语言，以及理解对方会如何理解你的表达。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 计算机系统和 CPU"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "计算机本身并不复杂，大致上就是三部分：\n",
    "* 负责执行指令的**中央处理器（CPU）**；\n",
    "* 负责存放 CPU 执行所需要数据的**内存**；\n",
    "* 各种**外设**，像硬盘、键盘、鼠标、显示设备、网卡等等这些都是。\n",
    "\n",
    "这里面 CPU 是核心的核心，因为它相当于人的大脑，我们教给计算机的指令实际上就是 CPU 在一条一条地执行，执行过程中会读写内存里的数据，会调用各种外设的接口（通过一种叫做“设备驱动”的软件模块）来完成数据的输入输出（I/O）操作。\n",
    "\n",
    "CPU 是一种计算机芯片，芯片可以执行的指令是在设计时就定好的， 不同芯片有不同的指令集，固化在硬件里，一般无法改变。因为大小、发热和功耗等问题，芯片的指令集不能面面俱到，而是有所侧重，比如一般计算机里 CPU 芯片比较擅长整数的运算，而 GPU（图形处理芯片，也就是显卡）比较擅长浮点数的运算，计算机图形处理尤其是 3D 图形处理会涉及大量的浮点运算，所以通常交给 GPU 去做，又快又省电；后来人们发现除了 3D 图形处理，人工智能等领域的一些科学计算也多是浮点运算，所以 GPU 也被拿来做这些计算工作。而我们的手机里的芯片，是把 CPU/GPU 还有其他一些做特殊工作的芯片集成在一块硅晶片上，这样可以节省空间，也能节约功耗，这种高度集成的芯片就叫 SoC（*System on Chip*）。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 汇编和编译器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "那么 CPU 执行的代码长啥样呢，我们可以瞄一眼，大致长这样：\n",
    "\n",
    "```nasm\n",
    "_add:                                   ## @add\n",
    "push rbp\n",
    "mov rbp, rsp\n",
    "mov dword ptr [rbp - 4], edi\n",
    "mov dword ptr [rbp - 8], esi\n",
    "mov esi, dword ptr [rbp - 4]\n",
    "add esi, dword ptr [rbp - 8]\n",
    "mov eax, esi\n",
    "pop rbp\n",
    "ret\n",
    "\n",
    "_main:                                  ## @main\n",
    "push rbp\n",
    "mov rbp, rsp\n",
    "sub rsp, 16\n",
    "mov dword ptr [rbp - 4], 0\n",
    "mov edi, 27\n",
    "mov esi, 15\n",
    "call _add\n",
    "add rsp, 16\n",
    "pop rbp\n",
    "ret\n",
    "```\n",
    "\n",
    "这叫汇编指令（*assembly*），和 CPU 实际执行的“机器指令（*machine code*）”几乎一样了，只是机器语言是二进制的，全是 01011001 这样，而汇编将其翻译成了我们稍微看得懂的一些指令和名字。机器可以直接执行的指令其实不多，在 Intel 官方的 [x64 汇编文档](https://software.intel.com/en-us/articles/introduction-to-x64-assembly) 里列出的常用指令码只有 20 多个，上图中的 `mov` `push` `pop` `add` `sub` `call` `ret` 等都是。我们目前并不需要搞懂这些都是干啥的，不过可以简单说一下，`mov` 就是把一个地方保存的数写到另一个地方，`call` 是调用另外一个代码段，`ret` 是返回 `call` 的地方继续执行。\n",
    "\n",
    "所以其实计算机执行的指令大致就是：\n",
    "* 把数放到某地方，可能在 CPU 内部也可能在内存里；\n",
    "* 对某地方的数进行特定运算（通常是加减乘除）；\n",
    "* 跳转到另一个地方执行一段指令；\n",
    "* 返回原来的位置继续执行。\n",
    "如此这般。\n",
    "\n",
    "可以想象，如果要完成复杂的任务，涉及复杂的逻辑流程，用汇编代码写起来可要费好大的劲儿了。但这就是机器的语言，CPU 实际上就只会这种语言，别的它都听不懂，怎么办？\n",
    "\n",
    "想来想去，是不是有可能我们用某种方式写下我们解决问题的方法，然后让计算机把这些方法翻译成上面那种计算机懂的指令呢？这种翻译的程序肯定不好写，但是一旦写好了以后所有人都可以用更接近人话的方式表达，计算机也能自己翻译给自己并且照做了，所谓辛苦一时享受一世，岂不美哉。程序员的典型思维就是这样：如果有个对人来说很麻烦的事情，就要试试看是不是可以让计算机来做。\n",
    "\n",
    "这个想法早已被证明完全可行，这样的翻译程序叫做“编译器（*compiler*）”。现在存在于世的编程语言有数百种，每一种都对应一个编译器，它可以读懂你用这语言写的程序，经过词法分析（*tokenizing*）、语法分析（*parsing*）、语义分析（*semantic analysis*）、优化（*optimization*）和代码生成（*code emission*）等环节，最终输出像上面那样机器可以看懂和执行的指令。\n",
    "\n",
    "编译理论和实现架构已经发展了很多年，已经非常成熟，并在不断优化中。只要愿意学习，任何人都可以设计自己的编程语言并为之实现一个编译器。\n",
    "\n",
    "顺便，上面的汇编代码是如下的 C 语言代码通过 LLVM/Clang 编译器的处理生成的：\n",
    "\n",
    "```c\n",
    "int add(int a, int b) {\n",
    "   return a + b;\n",
    "}\n",
    "\n",
    "int main() {\n",
    "   return add(27, 15);\n",
    "}\n",
    "```\n",
    "\n",
    "C 语言被公认是最接近机器语言的“高级编程语言”，因为 C 语言对内存数据的管理方式几乎和机器语言是一样的“手工操作”，需要编程者非常了解 CPU 对内存的管理模式。但尽管如此，C 语言还是高级编程语言，很接近我们人类的语言，基本上一看就懂。\n",
    "\n",
    "各种各样的高级编程语言，总有一样适合你，这些语言极大地降低了计算机编程的门槛，让几乎所有人都有机会编程，而这一切都是因为有编译器这样的“自动翻译”存在，建立了人类和机器之间畅通的桥梁。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 解释器和解释运行"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "前面我们介绍了编程语言编译为机器代码的原理，我们写好的程序可以被编译器翻译为机器代码，然后被机器运行。编译就好像把我们写好的文章整篇读完然后翻译为另一种语言，拿给会那个语言的人看。\n",
    "\n",
    "还有另外一种运行程序的方法，不需要整篇文章写完，可以一句一句的翻译，写一句翻一句执行一句，做这件事的也是一个程序，叫解释器（*interpreter*）。解释器和编译器的主要区别就在于，它读入源代码是一行一行处理的，读一行执行一行。\n",
    "\n",
    "解释器的好处是，可以实现一种交互式的编程，启动解释器之后，你说一句解释器就回你一句，有来有回，很亲切，出了问题也可以马上知道。现代人几乎不写信了，打电话和微信是主要的沟通模式，可能也是类似的道理吧。\n",
    "\n",
    "Python 就是一种解释型语言，在命令行界面运行 `python` 主程序，就会进入一个交互式的界面，输入一行程序立刻可以得到反馈，差不多就是这个样子："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"assets/python-repl.png\" width=\"500\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这就是 Python 的解释器界面，这种输入一行执行一行的界面有个通用的名字叫做 *REPL*（*read–eval–print loop* 的缩写），意思是这个程序可以读取（*read*）你的输入、计算（*evaluate*）、然后打印（*print*）结果，循环往复，直到你退出——在上图的界面中，输入 `exit()` 回车，就可以退出 Python 的 *REPL*。\n",
    "\n",
    "我们还可以把 Python 程序源代码（*source code*）存进一个文件里，然后让解释器直接运行这个文件。打开命令行界面（我们假定你已经[设置好了你的编程环境](x1-setup.md)），执行下面的操作：\n",
    "\n",
    "```shell\n",
    "cd ~/Code\n",
    "touch hello.py\n",
    "code hello.py\n",
    "```\n",
    "\n",
    "在打开的 VSCode 中输入下面的代码：\n",
    "\n",
    "```python\n",
    "print(f'4 + 9 = {4 + 9}')\n",
    "print('Hello world!')\n",
    "```\n",
    "\n",
    "保存，回到命令行界面执行：\n",
    "\n",
    "```python\n",
    "python hello.py\n",
    "```\n",
    "\n",
    "解释器会读取 `hello.py` 里面的代码，一行一行的执行并输出对应的结果。\n",
    "\n",
    "> 在有的系统中，`python` 不存在或者指向较老的 Python，可以通过 `python -V` 命令来查看其版本，如果返回 2.x 的话，可以将上述命令改为 `python3 hello.py`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "解释器的实现方式有好几种：\n",
    "* 分析源代码，直接执行（做某些解释器已经定义好的事情），或者\n",
    "* 把源代码翻译成某种特定的中间代码，然后交给一个专门运行这种中间代码的程序执行，这种程序通常叫虚拟机（*virtual machine*），或者\n",
    "* 把源代码编译为机器代码然后交给硬件执行。\n",
    "\n",
    "无论哪种方式，其实也会有词法分析、句法分析、语义分析、代码优化和生成等过程，和编译器的基本架构是相似的，事实上由于实现上的复杂性，有时候编译和解释之间并没有那么硬的界限。\n",
    "\n",
    "Python 官方解释器的工作原理是上列的第二种，即生成中间代码——叫做“字节码（*bytecode*）”——和虚拟机的方式；而另外有种 Python 的实现（[PyPy](http://pypy.org/)）采用的是第三种方式，即预编译为机器码的方式。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这本书里，绝大部分代码运行用的是 Jupyter Lab，它的目标是把 *REPL* 做的更好用，让我们可以在浏览器中使用，有更好的语法高亮显示，最棒的是，可以把我们交互编程的过程保存下来，与人共享。不过它背后还是 Python 解释器，和命令行界面下运行 `python` 没有本质区别（但用起来的愉快程度和效率还是有很大区别的）。\n",
    "\n",
    "> Jupyter Lab 通过一个可扩展的架构正在支持越来越多的文档类型和编程语言，以后学习很多别的语言也可以用它。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要使用 Jupyter Lab 需要先用 Python 的包管理工具 `pip` 来安装：\n",
    "\n",
    "```shell\n",
    "pip install jupyterlab\n",
    "```\n",
    "\n",
    "然后就可以用 `jupyter lab` 来运行 Jupyter Lab，在里面打开 *notebook* 进行交互式编程的尝试了，最好的办法是[使用我们的学习用书](x2-students-book.md)。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 小结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 程序是人类书写交由机器去执行的一系列指令，而编程语言是书写时遵照的语法规则；\n",
    "* 计算机 CPU 只能理解非常基础的指令，人要直接写出机器指令是困难和低效的；\n",
    "* 编译器和解释器是一些特殊的程序，能够把我们用高级编程语言编写的程序翻译成机器指令，然后交给计算机去执行；\n",
    "* 可以通过命令行 REPL、命令行加源文件和 Jupyter Lab 这样的可视化环境来运行 Python 程序，背后都是 Python 解释器。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
